package domain

import (
	"context"
	"fmt"
	"strings"
	"time"
)

type AuditLog struct {
	Id                string
	ApplicationName   *string
	UserId            *string
	UserName          *string
	ExecutionTime     time.Time
	ExecutionDuration int
	ClientIpAddress   *string
	ClientName        *string
	ClientId          *string
	BrowserInfo       *string
	HttpMethod        *string
	Url               *string
	Exceptions        *string
	HttpStatusCode    *int
	Actions           []*AuditLogAction
	EntityChanges     []*EntityChange
}

type AuditLogAction struct {
	Id                string
	AuditLogId        string
	ServiceName       *string
	MethodName        *string
	Parameters        *string
	ExecutionTime     time.Time
	ExecutionDuration int
}

type EntityChange struct {
	Id                 string
	AuditLogId         string
	ChangeTime         time.Time
	ChangeType         int
	EntityId           string
	EntityTypeFullName string
	PropertyChanges    []*EntityPropertyChange
}

type EntityPropertyChange struct {
	Id                   string
	EntityChangeId       string
	NewValue             *string
	OriginalValue        *string
	PropertyName         string
	PropertyTypeFullName string
}

type AuditLogRepository struct {
	sqlGdbc SqlGdbc
}

func NewAuditLogRepository(sqlGdbc SqlGdbc) *AuditLogRepository {
	return &AuditLogRepository{sqlGdbc}
}

func (r *AuditLogRepository) GetList(ctx context.Context, userName string, httpMethod string, httpStatusCode int, clientIpAddress string, executionTimeBegin time.Time, executionTimeEnd time.Time, url string, sorting string, pageIndex int, pageSize int) ([]*AuditLog, int64, error) {

	whereSql := ""
	params := make([]interface{}, 0)

	if userName != "" {
		whereSql += " and a.UserName = ? "
		params = append(params, userName)
	}

	if httpMethod != "" {
		whereSql += " and a.HttpMethod = ? "
		params = append(params, strings.ToUpper(httpMethod))
	}

	if httpStatusCode > 0 {
		whereSql += " and a.HttpStatusCode = ? "
		params = append(params, httpStatusCode)
	}

	if clientIpAddress != "" {
		whereSql += " and a.ClientIpAddress = ? "
		params = append(params, clientIpAddress)
	}

	if url != "" {
		whereSql += " and a.Url = ? "
		params = append(params, url)
	}

	if !executionTimeBegin.IsZero() {
		whereSql += " and a.ExecutionTime >= ? "
		params = append(params, executionTimeBegin)
	}

	if !executionTimeEnd.IsZero() {
		whereSql += " and a.ExecutionTime <= ? "
		params = append(params, executionTimeEnd)
	}

	//排序
	orderSql := ""
	sorts := strings.Split(sorting, " ")
	if len(sorts) > 0 {
		order := ""
		if len(sorts) == 2 {
			if sorts[1] == "desc" {
				order = "desc"
			} else if sorts[1] == "asc" {
				order = "asc"
			}
		}
		switch sorts[0] {
		case "ExecutionTime":
			orderSql = fmt.Sprintf(" order by ExecutionTime %s ", order)
		default:
			orderSql = " order by ExecutionTime desc "
		}
	}

	//分页
	pageSql := fmt.Sprintf("limit %v,%v", pageSize*pageIndex, pageSize)

	querySql := fmt.Sprintf(`select a.Id, a.ApplicationName, a.UserId, a.UserName, a.ExecutionTime, a.ExecutionDuration, a.ClientIpAddress, a.ClientName, a.ClientId, a.BrowserInfo, a.HttpMethod, a.Url, a.Exceptions, a.HttpStatusCode from abpauditlogs a where 1=1 %s %s %s`, whereSql, orderSql, pageSql)

	rows, err := r.sqlGdbc.QueryContext(ctx, querySql, params...)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	list := make([]*AuditLog, 0)
	for rows.Next() {
		entity := new(AuditLog)
		err = rows.Scan(&entity.Id, &entity.ApplicationName, &entity.UserId, &entity.UserName, &entity.ExecutionTime, &entity.ExecutionDuration, &entity.ClientIpAddress, &entity.ClientName, &entity.ClientId, &entity.BrowserInfo, &entity.HttpMethod, &entity.Url, &entity.Exceptions, &entity.HttpStatusCode)
		if err != nil {
			return nil, 0, err
		}
		list = append(list, entity)
	}

	err = rows.Err()
	if err != nil {
		return nil, 0, err
	}

	var count int64
	countSql := fmt.Sprintf(`select count(1) from abpauditlogs a where 1=1 %s`, whereSql)
	err = r.sqlGdbc.QueryRowContext(ctx, countSql, params...).Scan(&count)
	if err != nil {
		return nil, 0, err
	}

	return list, count, nil
}

func (r *AuditLogRepository) Get(ctx context.Context, id string, includeDetails bool) (*AuditLog, error) {

	entity := new(AuditLog)

	err := r.sqlGdbc.QueryRowContext(ctx, "SELECT Id, ApplicationName, UserId, UserName, ExecutionTime, ExecutionDuration, ClientIpAddress, ClientName, ClientId, BrowserInfo, HttpMethod, Url, Exceptions, HttpStatusCode FROM abpauditlogs where Id=?", id).Scan(&entity.Id, &entity.ApplicationName, &entity.UserId, &entity.UserName, &entity.ExecutionTime, &entity.ExecutionDuration, &entity.ClientIpAddress, &entity.ClientName, &entity.ClientId, &entity.BrowserInfo, &entity.HttpMethod, &entity.Url, &entity.Exceptions, &entity.HttpStatusCode)
	if err != nil {
		return nil, err
	}

	if includeDetails {
		//审计日志明细
		rows1, err := r.sqlGdbc.QueryContext(ctx, "SELECT Id, AuditLogId, ServiceName, MethodName, Parameters, ExecutionTime, ExecutionDuration FROM abpauditlogactions where AuditLogId = ?", id)
		if err != nil {
			return nil, err
		}
		defer rows1.Close()
		entity.Actions = make([]*AuditLogAction, 0)
		for rows1.Next() {
			action := new(AuditLogAction)
			err = rows1.Scan(&action.Id, &action.AuditLogId, &action.ServiceName, &action.MethodName, &action.Parameters, &action.ExecutionTime, &action.ExecutionDuration)
			if err != nil {
				return nil, err
			}
			entity.Actions = append(entity.Actions, action)
		}
		err = rows1.Err()
		if err != nil {
			return nil, err
		}

		//实体变更
		entity.EntityChanges = make([]*EntityChange, 0)
		rows2, err := r.sqlGdbc.QueryContext(ctx, "SELECT Id, AuditLogId, ChangeTime, ChangeType, EntityId, EntityTypeFullName FROM abpentitychanges where AuditLogId = ?", id)
		if err != nil {
			return nil, err
		}
		defer rows2.Close()
		for rows2.Next() {
			entityChange := new(EntityChange)
			err = rows2.Scan(&entityChange.Id, &entityChange.AuditLogId, &entityChange.ChangeTime, &entityChange.ChangeType, &entityChange.EntityId, &entityChange.EntityTypeFullName)
			if err != nil {
				return nil, err
			}

			//实体属性变更
			entityChange.PropertyChanges = make([]*EntityPropertyChange, 0)
			rows3, err := r.sqlGdbc.QueryContext(ctx, "SELECT Id, EntityChangeId, NewValue, OriginalValue, PropertyName, PropertyTypeFullName FROM abpentitypropertychanges where EntityChangeId = ?", entityChange.Id)
			if err != nil {
				return nil, err
			}
			for rows3.Next() {
				propertyChange := new(EntityPropertyChange)
				err = rows3.Scan(&propertyChange.Id, &propertyChange.EntityChangeId, &propertyChange.NewValue, &propertyChange.OriginalValue, &propertyChange.PropertyName, &propertyChange.PropertyTypeFullName)
				if err != nil {
					return nil, err
				}
				entityChange.PropertyChanges = append(entityChange.PropertyChanges, propertyChange)
			}
			err = rows3.Err()
			rows3.Close()
			if err != nil {
				return nil, err
			}
			entity.EntityChanges = append(entity.EntityChanges, entityChange)
		}
		err = rows2.Err()
		if err != nil {
			return nil, err
		}
	}

	return entity, nil
}
